#include "config.h"
#include <fstream>
#include "log.h"
#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

namespace daniel
{
    static sylar::Logger::ptr g_logger = DANIEL_LOG_NAME("system");
    Config::Config()
    {
    }
    Config::~Config()
    {
    }
    ConfigBase::ptr Config::lookupBase(const std::string &name)
    {
        auto it = m_configs.find(name);
        if (it == m_configs.end())
            return nullptr;
        else
        {
            return it->second;
        }
    }
    // output传入传出参数
    void Config::listAllMember(const std::string &prefix, const YAML::Node &node, std::list<std::pair<std::string, const YAML::Node>> &output)
    {
        if (prefix.find_first_not_of("abcdefghijklmnopqrstuvwxyz._0123456789") != std::string::npos)
        {
            DANIEL_LOG_DEBUG(g_logger) << "invalid prefix!";
            return;
        }
        //添加当前节点
        output.push_back(std::make_pair(prefix, node));
        //添加子节点
        if (node.IsMap())
        {
            for (auto it = node.begin(); it != node.end(); it++)
            {
                //如果是个常量 就是非数组和映射的量
                listAllMember(prefix.empty() ? it->first.Scalar() : prefix + "." + it->first.Scalar(), it->second, output);
            }
        }
    }

    // void Config::loadFromFile(const std::string &fileName = "/home/daniel/bin/config.yaml")
    // {
    //     //应该是一个Ojbect,需要把每层都拎出来，name格式为：
    //     // log    log.appender    log.formatter   等
    //     YAML::Node root = YAML::LoadFile(fileName);
    //     std::list<std::pair<std::string, const YAML::Node>> allNodes;
    //     listAllMember("", root, allNodes);
    //     //使用迭代器遍历object
    //     for (auto &item : allNodes)
    //     {
    //         std::string key = item.first;
    //         if (key.empty())
    //         {
    //             continue;
    //         }
    //         std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    //         //一开始需要注册  某种类型
    //         ConfigBase::ptr var = lookupBase(key);
    //         if (var)
    //         {
    //             if (item.second.IsScalar())
    //             {
    //                 var->fromString(item.second.Scalar());
    //             }
    //             else
    //             {
    //                 std::stringstream ss;
    //                 ss << item.second;
    //                 DANIEL_LOG_DEBUG(g_logger) << ss.str() << std::endl;
    //                 var->fromString(ss.str());
    //             }
    //         }
    //     }
    // }
    void Config::loadFromFile(const std::string &fileName)
    {
        YAML::Node root=YAML::LoadFile(fileName);
        for(auto it=root.begin();it!=root.end();it++){
            //从现有配置中，是否包含fileName的名字，如包含更新值
            auto name=it->first.as<std::string>();
            auto configIt=m_configs.find(name);
            if (configIt==m_configs.end()){
                //说明文件中有，但是现有配置中无
                DANIEL_LOG_DEBUG(g_logger)<<"file consists of the config, but the configMsg don't register the config name:"<<name;
            }
            else{
                std::stringstream ss;
                ss<<it->second;
                DANIEL_LOG_DEBUG(g_logger)<<ss.str()<<std::endl;
                ConfigBase::ptr var=lookupBase(name);
                var->fromString(ss.str());
            }
        }
    }
    void Config::loadFromDir(const std::string& dirName)
    {
        DIR* dir_fd=::opendir(dirName.c_str());
        if (dir_fd==nullptr){
            DANIEL_LOG_WARN(g_logger)<<"dirName is invalid!";
            return;
        }
        //需要找到所有后缀名为yml的文件，然后拼接为完成的路径  然后调用loadFromFile()
        struct dirent* dir_info;
        while ((dir_info=readdir(dir_fd))!=nullptr)
        {
            /* code */
            std::string fileName(dir_info->d_name);
            size_t length=fileName.size();
            if (length>4 && fileName.substr(length-4,4)==std::string(".yml")){
                std::string fullPath;
                //防止输入文件夹最后带 “/"
                if (dirName.substr(length-1,1)=="/"){
                    fullPath=dirName+fileName;
                }
                else {
                    fullPath=dirName+"/"+fileName;
                }
                DANIEL_LOG_DEBUG(g_logger)<<fullPath;
                loadFromFile(fullPath);
            }
        }
        
    }

    void Config::printAll()
    {
        for (const auto &item : m_configs)
        {
            std::cout << item.second->toString() << std::endl;
        }
    }
}